package ar.edu.utn.frsf.cidisi.ginet.generation.abstract2concrete;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.pnml.tools.epnk.pnmlcoremodel.AnnotationGraphics;
import org.pnml.tools.epnk.pnmlcoremodel.Coordinate;
import org.pnml.tools.epnk.pnmlcoremodel.Name;
import org.pnml.tools.epnk.pnmlcoremodel.Node;
import org.pnml.tools.epnk.pnmlcoremodel.NodeGraphics;
import org.pnml.tools.epnk.pnmlcoremodel.Page;
import org.pnml.tools.epnk.pnmlcoremodel.PetriNet;
import org.pnml.tools.epnk.pnmlcoremodel.PetriNetDoc;
import org.pnml.tools.epnk.pnmlcoremodel.PetriNetType;
import org.pnml.tools.epnk.pnmlcoremodel.PnmlcoremodelFactory;
import org.pnml.tools.epnk.pnmlcoremodel.impl.NodeGraphicsImpl;
import org.pnml.tools.epnk.pntypes.hlpng.pntd.hlpngdefinition.Declaration;
import org.pnml.tools.epnk.pntypes.hlpng.pntd.hlpngdefinition.HLAnnotation;
import org.pnml.tools.epnk.pntypes.hlpng.pntd.hlpngdefinition.HlpngdefinitionFactory;
import org.pnml.tools.epnk.pntypes.hlpng.pntd.hlpngdefinition.Type;
import org.pnml.tools.epnk.pntypes.hlpngs.datatypes.integers.IntegersFactory;
import org.pnml.tools.epnk.pntypes.hlpngs.datatypes.terms.Declarations;
import org.pnml.tools.epnk.pntypes.hlpngs.datatypes.terms.NamedSort;
import org.pnml.tools.epnk.pntypes.hlpngs.datatypes.terms.Sort;
import org.pnml.tools.epnk.pntypes.hlpngs.datatypes.terms.TermsFactory;
import org.pnml.tools.epnk.pntypes.hlpngs.datatypes.terms.UserSort;
import org.pnml.tools.epnk.pntypes.hlpngs.datatypes.terms.Variable;
import org.pnml.tools.epnk.pntypes.hlpngs.datatypes.terms.VariableDecl;

import ar.edu.utn.frsf.cidisi.ginet.Arc;
import ar.edu.utn.frsf.cidisi.ginet.GINet;
import ar.edu.utn.frsf.cidisi.ginet.GinetFactory;
import ar.edu.utn.frsf.cidisi.ginet.Place;
import ar.edu.utn.frsf.cidisi.ginet.Transition;
import ar.edu.utn.frsf.cidisi.ginet.abstractginet.AbstractGINet;
import ar.edu.utn.frsf.cidisi.ginet.abstractginet.RefPlace;

/**
 * @author jmr
 *
 */
public class AbstractGIM2ConcreteGIM {
	
	private PetriNetDoc petriNetDoc;

	private GINet concreteGINet;
	
	private AbstractGINet abstractGINet;
	
	private PetriNetType pnType;
	
//	/**
//	 * At the moment, the definition's page is hard coded by this attribute, but in the future 
//	 * this attribute should be replaced by a mechanism to indicate that this page is the one to make all 
//	 * the common definitions of the GI-Net.
//	 */
//	private ar.edu.utn.frsf.cidisi.ginet.Page definitionsPage = null;
	
//	private Page abstractGIModule;
//	private Page concreteGIModule;
	

	/**
	 *  Map to access the concrete places of an abstract GI module. 
	 *  The key of the map is the place's id. 
	 */
	private Map<String, Place> concretePlacesMap = new HashMap<String, Place>();

	/**
	 *  Map to access the concrete transitions of an abstract GI module. 
	 *  The key of the map is the transitions's id. 
	 */
	private Map<String, Transition> concreteTransitionsMap = new HashMap<String, Transition>();

	/**
	 *  Map to access the concrete reference places of an abstract GI module. 
	 *  The key of the map is the transitions's id. 
	 */
	private Map<String, org.pnml.tools.epnk.pnmlcoremodel.RefPlace> concreteRefPlacesMap = new HashMap<String, org.pnml.tools.epnk.pnmlcoremodel.RefPlace>();

	/**
	 *  Map to access the subpages of an abstract GI module. 
	 *  The key of the map is the subpage's id. 
	 */
	private Map<String, Page> concreteSubPagesMap = new HashMap<String, Page>();
		
	/**
	 * Creates an instance of {@code AbstractGIM2ConcreteGIM} based on the abstract GI-Net aGINet. 
	 * 
	 * @param aGINet This is the abstract GI-Net which is the source of the transformation.
	 */
	public AbstractGIM2ConcreteGIM(AbstractGINet aGINet){
		
		// This is the abstract GI-Net which is the source of the transformation method.-
		abstractGINet = aGINet;

		// Creates the Petri Net document and the concrete GI-Net which is the target of the transformation method.-
		petriNetDoc = PnmlcoremodelFactory.eINSTANCE.createPetriNetDoc();
		concreteGINet = createConcreteGINet();
		petriNetDoc.getNet().add(concreteGINet);
		

		// Once the concrete GI-Net is created, it is necessary to add the declarations defined in the abstract GI-Net.-
		EList<Declaration> declaration = abstractGINet.getDeclaration(); 
		EList<Declaration> newDeclarations = createDeclarations(declaration);
		concreteGINet.getDeclaration().addAll(newDeclarations);
		
	}
	
	/**
	 * Creates the PNML file.
	 * 
	 * @param resource The full path (including file name) to the new PNML file
	 * @return
	 *
	 * @author jmr
	 */
	public Resource createPNMLFile(String path) {

		// create the resource and add the net to it
		ResourceSet resourceSet = new ResourceSetImpl();
		
		final URI uri = URI.createURI("platform:"+path);
		
		final Resource newResource = resourceSet.createResource(uri);
		
		EList<EObject> contents = newResource.getContents();
		
		contents.add(petriNetDoc);
		
		try {
			
			newResource.save(new HashMap<Object, Object>());
			
		} catch (IOException e) {
			
			return null;
			
		}
		
		return newResource;

	}
	
	/**
	 * Creates the concrete GI-Net.
	 * 
	 * @return
	 *
	 * @author jmr
	 */
	private GINet createConcreteGINet(){
		
		GINet cginet = GinetFactory.eINSTANCE.createGINet();
		
		cginet.setId("n1");
		
//		type = (PetriNetType) GinetFactory.eINSTANCE.createGINet();
		pnType = (PetriNetType) GinetFactory.eINSTANCE.createGINetType();
//		type = (PetriNetType) PtnetFactory.eINSTANCE.createPTNet();
		
		cginet.setType(pnType);
		
		Name nameLabel = PnmlcoremodelFactory.eINSTANCE.createName();
		
		nameLabel.setText("Concrete GI-Net");
		
		cginet.setName(nameLabel);
		
		return cginet;

	}
	
	

	/**
	 * Creates a page for a concrete GI module
	 * 
	 * @param pnType Petri Net type
	 * @param id Page id
	 * @param name Page name
	 * @return A Page that represents the concrete GI module
	 *
	 * @author jmr
	 */
	public Page createPage(Page agim, String id, String name) {

	
		Page page = pnType.createPage();
		
		page.setId(id);
		
		Name nameLabel = PnmlcoremodelFactory.eINSTANCE.createName();
		
		nameLabel.setText(name);
		
		page.setName(nameLabel);
		
		if (agim.getGraphics() != null){
		
			NodeGraphics nodeGraphics = PnmlcoremodelFactory.eINSTANCE.createNodeGraphics();
			
			Coordinate position = ((NodeGraphicsImpl)agim.getGraphics()).getPosition();
//			float posX = position.getX();
//			float posY = position.getY();
	
			// Creates a coordinate position for the page
			Coordinate newPosition = PnmlcoremodelFactory.eINSTANCE.createCoordinate();
			newPosition.setX(position.getX());
			newPosition.setY(position.getY());
			nodeGraphics.setPosition(newPosition);
	
			page.setGraphics(nodeGraphics);
			
			// Creates a label for the page
			AnnotationGraphics labelPos = PnmlcoremodelFactory.eINSTANCE.createAnnotationGraphics();
			Coordinate offset = PnmlcoremodelFactory.eINSTANCE.createCoordinate();
			offset.setX(0);
			offset.setY(-30);
			labelPos.setOffset(offset);
			nameLabel.setGraphics(labelPos);
			page.setName(nameLabel);
			

		}
		
		return page;
		
	}
	
	/**
	 * Creates and adds a new concrete GI module from an abstract GI module.
	 * Creates a new concrete GI module from an abstract GI module and adds it to the concrete GI-Net.
	 * A concrete module with its corresponding places, transitions, arcs, as well as type and variable 
	 * declarations is generated from the abstract GI module received as a parameter.
	 * If the abstract module contains submodules, for each of these submodules a set of concrete modules is generated.
	 * 
	 * @param agim The abstract GI module from which the concrete GI module is generated
	 * @return A concrete GI module
	 *
	 * @author jmr
	 */
	public Page addConcreteGIModule(ar.edu.utn.frsf.cidisi.ginet.abstractginet.Page agim){
		
		Page cgim = createConcreteGIModule(concreteGINet, agim);
		
//		// Adds the new concrete page to the GI-Net 
//		concreteGINet.getPage().add(cgim);
		
		return cgim;
	}

	
//	/**
//	 * Creates a new concrete GI module from an abstract GI module and adds it to an existing concrete GI module.
//	 * A concrete module with its corresponding places, transitions, arcs, as well as type and variable 
//	 * declarations is generated from the abstract GI module received as a parameter.
//	 * If the abstract module contains submodules, for each of these submodules a set of concrete modules is generated.
//	 * 
//	 * @param agim The abstract GI module from which the concrete GI module is generated
//	 * @param cgim The concrete GI module parent of the new concrete GI module
//	 * @return A concrete GI module
//	 *
//	 * @author jmr
//	 */
//	public Page addConcreteGIModuleTo(Page agim, Page cgim){
//		
//		Page newCgim = createConcreteGIModule(agim);
//		
//		// Adds the new concrete subpage to the parent page cgim
//		cgim.getObject().add(newCgim);
//		
//		return newCgim;
//	}

	public Page createConcreteGIModule(EObject parent, ar.edu.utn.frsf.cidisi.ginet.abstractginet.Page agim){
		return createConcreteGIModule(0, parent, agim, 0, 0);
	}
	
	/**
	 * 
	 * @param parent Is the parent of the concrete GI module to be created. It could be a PetriNet or a Page
	 * @param agim Is the abstract GI module that defines the concrete GI module to be created
	 * @return
	 *
	 * @author jmr
	 */
	public Page createConcreteGIModule(int index, EObject parent, ar.edu.utn.frsf.cidisi.ginet.abstractginet.Page agim, float x, float y){

		String newPageId = agim.getId();
		if (index >= 0)
			newPageId += index;
		// Creates the page that represents the concrete GI module
		Page newCgim = createPage(agim, newPageId.toLowerCase(), agim.getName().getText()+index, x, y);
		
		if (parent != null && parent instanceof PetriNet){
			
			((PetriNet)parent).getPage().add(newCgim);
			
			if (parent instanceof PetriNet){
				
				newCgim.setId(((PetriNet)parent).getId());
				
			}

		} else if (parent instanceof org.pnml.tools.epnk.pntypes.hlpng.pntd.hlpngdefinition.Page){
			
			newCgim.setId((((org.pnml.tools.epnk.pntypes.hlpng.pntd.hlpngdefinition.Page)parent).getId()+newPageId).toLowerCase());
			
			((org.pnml.tools.epnk.pntypes.hlpng.pntd.hlpngdefinition.Page)parent).getObject().add(newCgim);
		}
		
	
		
//		int sequenceCardinality = 1;
//		if (agim.getSequentialCardinality()!=null && agim.getSequentialCardinality()!="")
//			sequenceCardinality = Integer.valueOf(agim.getSequentialCardinality()).intValue();
		
//		if (sequenceCardinality!=1){
//			
//			newCgim = createSequencedConcreteGIModule(newCgim, agim, sequenceCardinality);
//			
//		} else {
		
			// Get places from the abstract GI module
			EList<ar.edu.utn.frsf.cidisi.ginet.abstractginet.Place> places = ((ar.edu.utn.frsf.cidisi.ginet.abstractginet.Page)agim).getPlaces();
	
			// Create places for the concrete GI module
			EList<Place> newPlaces = createPlaces(newCgim, places);
			newCgim.getObject().addAll(newPlaces);
	//		places.put(source.getName().getText(), source);		
	
			
			
			// Get transitions from the abstract GI module
			EList<ar.edu.utn.frsf.cidisi.ginet.abstractginet.Transition> transitions = ((ar.edu.utn.frsf.cidisi.ginet.abstractginet.Page)agim).getTransitions();
			
			// Create transitions for the concrete GI module
			EList<Transition> newTransitions = createTransitions(newCgim, transitions);
			newCgim.getObject().addAll(newTransitions);
	
	
			
			// Get subpages from the abstract GI module
			EList<ar.edu.utn.frsf.cidisi.ginet.abstractginet.Page> subPages = ((ar.edu.utn.frsf.cidisi.ginet.abstractginet.Page)agim).getPages();
			
			// Create subpages for the concrete GI module
			EList<Page> newSubPages = createSubPages(newCgim, subPages);
			newCgim.getObject().addAll(newSubPages);
	
			
			
			// Get referenced pages from the abstract GI module
			EList<ar.edu.utn.frsf.cidisi.ginet.abstractginet.Transition> referringTransitions = ((ar.edu.utn.frsf.cidisi.ginet.abstractginet.Page)agim).getReferringTransitions();
			
			// Create referenced pages for the concrete GI module
			newSubPages = createReferencedPages(newCgim, agim, referringTransitions);
			newCgim.getObject().addAll(newSubPages);
	
			
	
			// Get refplaces from the abstract GI module
//			EList<RefPlace> refPlaces = ((ar.edu.utn.frsf.cidisi.ginet.Page)agim).getRefPlaces();
			EList<RefPlace> refPlaces = ((ar.edu.utn.frsf.cidisi.ginet.abstractginet.Page)agim).getRefPlaces();
			
			// Create transitions for the concrete GI module
			EList<org.pnml.tools.epnk.pnmlcoremodel.RefPlace> newRefPlaces = createRefPlaces(newCgim,agim,index,refPlaces);
			newCgim.getObject().addAll(newRefPlaces);
	
	
			
			// Get arcs from the abstract GI module
			EList<ar.edu.utn.frsf.cidisi.ginet.abstractginet.Arc> arcs = ((ar.edu.utn.frsf.cidisi.ginet.abstractginet.Page)agim).getArcs();
	
			// Create arcs for the concrete GI module
			EList<Arc> newArcs = createArcs(newCgim, arcs);
			newCgim.getObject().addAll(newArcs);
//		}
		

		return newCgim;
	}
	
	
	/**
	 * 
	 * @param cgim
	 * @param card
	 * @return
	 *
	 * @author jmr
	 */
	private Page createSequencialConcreteGIModule(int index, EObject parent, ar.edu.utn.frsf.cidisi.ginet.abstractginet.Page agim, float x, float y, Page cgim, ar.edu.utn.frsf.cidisi.ginet.Transition referringTransition, int card){
			
		String newPageId = agim.getId();
		if (index >= 0)
			newPageId += index;
		// Creates the page that represents the concrete GI module
		Page newCgim = createPage(agim, newPageId.toLowerCase(), agim.getName().getText()+index, x, y);
		
		if (parent != null && parent instanceof PetriNet){
//			((PetriNet)parent).getPage().add(newCgim);
//			newCgim.setId(((PetriNet)parent).getId());
			// Adds the concrete GI module to its parent
		} else if (parent instanceof org.pnml.tools.epnk.pntypes.hlpng.pntd.hlpngdefinition.Page){
			newCgim.setId((((org.pnml.tools.epnk.pntypes.hlpng.pntd.hlpngdefinition.Page)parent).getId()+newPageId).toLowerCase());
			((org.pnml.tools.epnk.pntypes.hlpng.pntd.hlpngdefinition.Page)parent).getObject().add(newCgim);
		}
		
		// Create color and variable declarations
		EList<Declaration> newDeclarations = createDeclarations(((ar.edu.utn.frsf.cidisi.ginet.Page)agim).getDeclaration());
		((org.pnml.tools.epnk.pntypes.hlpng.pntd.hlpngdefinition.Page)newCgim).getDeclaration().addAll(newDeclarations);
		
		
//		int sequenceCardinality = 1;
//		if (agim.getSequenceCardinality()!=null && agim.getSequenceCardinality()!="")
//			sequenceCardinality = Integer.valueOf(agim.getSequenceCardinality()).intValue();		
//		ar.edu.utn.frsf.cidisi.ginet.Place aInputPlace = referringTransition.getIn()
		ar.edu.utn.frsf.cidisi.ginet.abstractginet.Place aInputPlace = agim.getInputPlace();
		
//		Place cInputPlace = getConcreteInputPlaceOf(newCgim, referringTransition);
		
		float xPos = ((NodeGraphics)aInputPlace.getGraphics()).getPosition().getX();
		float yPos = ((NodeGraphics)aInputPlace.getGraphics()).getPosition().getY();
		
		Place cInputPlace = createPlace(pnType, newCgim.getId().toLowerCase()+aInputPlace.getName().getText()+"0", aInputPlace.getName().getText()+"0", 0, xPos, yPos, aInputPlace.getType());
		concretePlacesMap.put(cInputPlace.getId(), cInputPlace);
		newCgim.getObject().add(cInputPlace);
		
		float mostRightXPos = agim.getMostRightXPos();
		
		for (int i = 0;i<card;i++){
			
			createSequencialConcreteGIModuleElements(newCgim, aInputPlace, cInputPlace, i, mostRightXPos);
			
		}
		
		return newCgim;
	}
	
	/**
	 * Creates the elements (places, transitions, arcs, ref places, pages) of a sequential page 
	 * 
	 * @param cgim A concrete page
	 * @param cNode The starting element of the sequence
	 * @param index The index of current sequence. It is useful to update the offset of the X coordinates of the elements of a page 
	 *
	 * @author jmr
	 */
	private void createSequencialConcreteGIModuleElements(Page cgim, Node aNode, Node cNode, int index, float mostRightXPos){
		
		for (org.pnml.tools.epnk.pnmlcoremodel.Arc arc:aNode.getOut()){
			
			if (arc.getTarget() instanceof ar.edu.utn.frsf.cidisi.ginet.abstractginet.Place){
				
				ar.edu.utn.frsf.cidisi.ginet.abstractginet.Place place = (ar.edu.utn.frsf.cidisi.ginet.abstractginet.Place)arc.getTarget();
				
				float xPos = ((NodeGraphics)place.getGraphics()).getPosition().getX();

				// Change the abstract place's X coordinate to reflect the offset on the X axis. This is necessary to reuse the method createsPlaces(...)
				((NodeGraphics)place.getGraphics()).getPosition().setX(((NodeGraphics)place.getGraphics()).getPosition().getX()+((mostRightXPos+15)*index));
				
				EList<ar.edu.utn.frsf.cidisi.ginet.abstractginet.Place> places = new BasicEList<ar.edu.utn.frsf.cidisi.ginet.abstractginet.Place>();
				
				places.add(place);
				
				EList<Place> newCPlaces = createPlaces(cgim, places);
				
				cgim.getObject().addAll(newCPlaces);
				
			} else if (arc.getTarget() instanceof ar.edu.utn.frsf.cidisi.ginet.Transition){
				
				ar.edu.utn.frsf.cidisi.ginet.abstractginet.Transition transition = (ar.edu.utn.frsf.cidisi.ginet.abstractginet.Transition)arc.getTarget();
				
//				float xPos = ((NodeGraphics)transition.getGraphics()).getPosition().getX();
//				float yPos = ((NodeGraphics)transition.getGraphics()).getPosition().getY();
//				// Change the abstract transition's X coordinate to reflect the offset on the X axis. This is necessary to reuse the method createsPlaces(...)
//				((NodeGraphics)transition.getGraphics()).getPosition().setX(xPos+((mostRightXPos+15)*index));
				
				ar.edu.utn.frsf.cidisi.ginet.abstractginet.Transition tempTransition = transition.clone();
				
				EList<ar.edu.utn.frsf.cidisi.ginet.abstractginet.Transition> transitions = new BasicEList<ar.edu.utn.frsf.cidisi.ginet.abstractginet.Transition>();
				
				transitions.add(tempTransition);
				
				EList<Transition> newCTransions = createTransitions(cgim, transitions);
				
				cgim.getObject().addAll(newCTransions);

			} else if (arc.getTarget() instanceof ar.edu.utn.frsf.cidisi.ginet.Page){
				
				
			} else if (arc.getTarget() instanceof RefPlace){
				
				
			} else if (arc.getTarget() instanceof ar.edu.utn.frsf.cidisi.ginet.Arc){
				
				
			}
			
		}
		
	}
	
//	private Place getConcreteInputPlaceOf(Page cgim, ar.edu.utn.frsf.cidisi.ginet.Transition transition){
//		
//		for (org.pnml.tools.epnk.pnmlcoremodel.Arc arc:transition.getIn()){
//			
//			if (arc.getSource() instanceof RefPlace){
//				RefPlace abstractRefPlace = (RefPlace)arc.getSource();
//				
//				return getReferencedPlace(cgim, abstractRefPlace, 0);
//			}
//			
//		}
//		
//		return null;
//	}
	
	
//	private Place getAbstractInputPlace(ar.edu.utn.frsf.cidisi.ginet.Page agim, ar.edu.utn.frsf.cidisi.ginet.Transition transition){
//		
//		ar.edu.utn.frsf.cidisi.ginet.Page page = transition.getRefPage();
//		
////		ar.edu.utn.frsf.cidisi.ginet.Place abstractPlace = transition.
//		
//		for (org.pnml.tools.epnk.pnmlcoremodel.Arc arc:transition.getIn()){
//			
//			if (arc.getSource() instanceof Place){
//				Place abstractPlace = (Place)arc.getSource();
//				
//				return getReferencedPlace(cgim, abstractPlace, 0);
//			}
//			
//		}
//		
//		return null;
//	}

	/**
	 * Sets the cardinality of every place and transition that is defined as an abstract place
	 * 
	 * @param agim An abstract GI module
	 * @param card An integer that defines the cardinality to be assigned to the abstract GI module 
	 *
	 * @author jmr
	 */
	public void setCardinality(ar.edu.utn.frsf.cidisi.ginet.abstractginet.Page agim, int card){
		
		// Assigns cardinality to each abstract place
		EList<ar.edu.utn.frsf.cidisi.ginet.abstractginet.Place> places = agim.getPlaces();
		
		for (ar.edu.utn.frsf.cidisi.ginet.abstractginet.Place place:places){
			
			// If this is an abstract place
			if (place.getParallelCardinality()!=null && place.getParallelCardinality().equals("-1")){
				
				place.setParallelCardinality(""+card);
				
			}
		}
		
		// Assigns cardinality to each abstract transition
		EList<ar.edu.utn.frsf.cidisi.ginet.abstractginet.Transition> transitions = agim.getTransitions();
		
		for (ar.edu.utn.frsf.cidisi.ginet.abstractginet.Transition transition:transitions){
			
			// If this is an abstract transition
			if (transition.getParallelCardinality()!=null && transition.getParallelCardinality().equals("-1")){
				
				transition.setParallelCardinality(""+card);
				
			}
		}

	}
	

	/**
	 * Create color (type) and variable declarations from existing declarations received as a parameter.
	 * 
	 * @param declarations Existing declarations
	 * @return A list of declarations
	 *
	 * @author jmr
	 */
	private EList<Declaration> createDeclarations(EList<Declaration> declarations){
		EList<Declaration> newDecls = new BasicEList<Declaration>();
		
		Map<String, NamedSort> declarationsMap = 
				new HashMap<String, NamedSort>();
		
		for (Declaration decl : declarations){
			
			Declaration newDecl = HlpngdefinitionFactory.eINSTANCE.createDeclaration();
			
			Declarations newInnerDecls = TermsFactory.eINSTANCE.createDeclarations();
			
			Declarations innerDecls = decl.getStructure();
			
			for (org.pnml.tools.epnk.pntypes.hlpngs.datatypes.terms.Declaration innerDecl : innerDecls.getDeclaration()){

				if (innerDecl instanceof NamedSort){
					Sort sort; 
					NamedSort newInnerDecl = TermsFactory.eINSTANCE.createNamedSort();
					
					newInnerDecl.setDef(((NamedSort)innerDecl).getDef().clone());
					
					newInnerDecl.setName(innerDecl.getName());
					
					newInnerDecl.setId(innerDecl.getId());
					
					newInnerDecls.getDeclaration().add(newInnerDecl);
					
					declarationsMap.put(newInnerDecl.getName(), newInnerDecl);
					
				} else if (innerDecl instanceof VariableDecl){

					VariableDecl newInnerDecl = TermsFactory.eINSTANCE.createVariableDecl();
					((UserSort)((VariableDecl) innerDecl).getSort()).getDeclaration();
//					NamedSort namedSort = declarationsMap.get(((UserSort)((VariableDecl) innerDecl).getSort()).getName());
//					NamedSort namedSort = declarationsMap.get(((VariableDecl) innerDecl).getName());
					NamedSort namedSort = declarationsMap.get(((UserSort)((VariableDecl) innerDecl).getSort()).getDeclaration().getName());
					
//					namedSort.getDef().
					
//					ACA TENES QUE VER COMO TOMAR EL SORT QUE YA ESTA ALMACENADO EN EL ARCHIVO ANTERIOR.
//					TAMBIEN HAY QUE VER QUE LOS SORT SE CREEN PRIMERO QUE LAS VARIABLES. SINO NO VAN A PODER 
//					SER REFERENCIADAS Y VA A QUEDAR LA REFERENCIA AL ARHCIVO DE UPCOLBPIP 
					
//					((VariableDecl) innerDecl).getSort()
					
					UserSort userSort = TermsFactory.eINSTANCE.createUserSort();
					userSort.setDeclaration(namedSort);
					userSort.setName(namedSort.getName());
					
					newInnerDecl.setSort(userSort);
					
					newInnerDecl.setName(innerDecl.getName());
					
					newInnerDecl.setId(innerDecl.getId());
					
					newInnerDecls.getDeclaration().add(newInnerDecl);

				}
				
			}
			newDecl.setStructure(newInnerDecls);
			newDecls.add(newDecl);
		}
		
		return newDecls;
	}

	
	/**
	 * Creates a set of concrete places according to the cardinality defined for each abstract place in the list abstract places.
	 * 
	 * @param places List of places of an abstract GI-Net
	 * @return List of concrete places 
	 *
	 * @author jmr
	 */
	private EList<Place> createPlaces(Page cgim, EList<ar.edu.utn.frsf.cidisi.ginet.abstractginet.Place> places){
		EList<Place> np = new BasicEList<Place>();
		
		// For each place in the list of abstract places
		for (ar.edu.utn.frsf.cidisi.ginet.abstractginet.Place place : places){
			
			int card = 1;
			if (place.getParallelCardinality() != null && !place.getParallelCardinality().equals("")){
				card = Integer.valueOf(place.getParallelCardinality()).intValue();
			}
			
			if (card > 1){  // Check the cardinality
				
				// Creates a number of places determined by the cardinality of the current abstract place
				for (int i=0; i<card; i++){
					
					Coordinate position = ((NodeGraphicsImpl)place.getGraphics()).getPosition();
					
					String id = place.getName().getText();
					
					float yPos = position.getY();
					
					id += i;		// Updates the id
					yPos += 90*i;	// Updates the y coordinate
					
					// Creates a new place
					Place newPlace = createPlace(pnType, cgim.getId()+id, place.getName().getText()+i, 0, position.getX(), yPos, place.getType());
					
					// Add the newly created place to a map in order to have direct access to such place
					concretePlacesMap.put(newPlace.getId(), newPlace);
					
					np.add(newPlace);
					
				}
				
			} else { // Creates one place
				
				Coordinate position = ((NodeGraphicsImpl)place.getGraphics()).getPosition();
				
				Place newPlace = createPlace(pnType, cgim.getId()+place.getName().getText()+"0", place.getName().getText()+"0", 
						0, position.getX(), position.getY(), place.getType());

				concretePlacesMap.put(newPlace.getId(), newPlace);

				np.add(newPlace);
				
			}
			
		}
		
		return np;
	}


	/**
	 * Creates a set of concrete transitions according to the cardinality defined for each 
	 * transition in the list abstract transitions.
	 * 
	 * @param abstractTransitions List of transitions of an abstract GI-Net
	 * @return List of concrete transitions 
	 *
	 * @author jmr
	 */
	private EList<Transition> createTransitions(Page parentCgim, EList<ar.edu.utn.frsf.cidisi.ginet.abstractginet.Transition> abstractTransitions){
		EList<Transition> nt = new BasicEList<Transition>();
		
		// For each place in the list of abstract places
		for (ar.edu.utn.frsf.cidisi.ginet.abstractginet.Transition abstractTransition : abstractTransitions){
			
//			OJO ACA CON ESTO!!!!
			int sequenceCardinality = 1;
			if (abstractTransition.getSequentialCardinality()!=null
					&& abstractTransition.getSequentialCardinality()!=""){
				
				sequenceCardinality = Integer.valueOf(abstractTransition.getSequentialCardinality()).intValue();
				
			}

			// Transitions referencing a sequence page are not associated with any concrete transition
			if (sequenceCardinality==1){
				
				int card = 1;
				if (abstractTransition.getParallelCardinality() != null && !abstractTransition.getParallelCardinality().equals("")){
					card = Integer.valueOf(abstractTransition.getParallelCardinality()).intValue();
				}
				
				if (card > 1){  // Check the cardinality
					
					// Creates a number of transitions determined by the cardinality of the current abstract place
					for (int i=0; i<card; i++){
						
						Coordinate position = ((NodeGraphicsImpl)abstractTransition.getGraphics()).getPosition();
						
						String id = abstractTransition.getName().getText();
						
						float yPos = position.getY();
						
						id += i;		// Updates the id
						yPos += 90*i;	// Updates the y coordinate
	
						// Creates a new transition
						Transition newTransition = createTransition(pnType, parentCgim.getId()+id, abstractTransition.getName().getText()+i, position.getX(), yPos);
						
						// Add the newly created transition to a map in order to have direct access to such transition
						concreteTransitionsMap.put(newTransition.getId(), newTransition);
	
						nt.add(newTransition);
						
//						Page newCgim = createConcreteGIModule(i, parentCgim, abstractSubPage, position.getX(), yPos);
//						
//						newTransition.set
//						
//						// Add the newly created subpage to a map in order to have direct access to such subpage
//						concreteSubPagesMap.put(newCgim.getId(), newCgim);
//
//						parentCgim.getObject().add(newCgim);						
						
					}
					
				} else {// Creates one transition
					
					Coordinate position = ((NodeGraphicsImpl)abstractTransition.getGraphics()).getPosition();
					
					Transition newTransition = createTransition(pnType, parentCgim.getId()+abstractTransition.getName().getText()+"0", abstractTransition.getName().getText()+"0", 
							position.getX(), position.getY());
	
					concreteTransitionsMap.put(newTransition.getId(), newTransition);
	
					nt.add(newTransition);
					
				}
			}
			
		}
		
		return nt;
	}

	/**
	 * Creates the set of arcs that connects places and transitions of a concrete GI-Net.
	 * 
	 * @param arcs A set of arcs of an abstract GI module
	 * @param cgim A concrete GI module
	 * @return A set of arcs which are part of a concrete GI module
	 *
	 * @author jmr
	 */
	private EList<Arc> createArcs(Page cgim, EList<ar.edu.utn.frsf.cidisi.ginet.abstractginet.Arc> arcs){
		
		EList<Arc> newArcs = new BasicEList<Arc>();
		
		// Iterates over all arcs of the Abstract GI module
		for (ar.edu.utn.frsf.cidisi.ginet.abstractginet.Arc arc : arcs){
			
			// Get source and target of current arc
			Node sourceNode = arc.getSource();
			Node targetNode = arc.getTarget();
			
			int sourceCardinality = 1;
			int targetCardinality = 1;
			
			boolean isSequence = false;
			
			sourceCardinality = getParallelCardinality(sourceNode);
			
			if (getSequentialCardinality(sourceNode)!=1)
				isSequence = true;

			targetCardinality = getParallelCardinality(targetNode);
			
			if (getSequentialCardinality(targetNode)!=1)
				isSequence = true;

			
			if (!isSequence){
			
				if (sourceCardinality > 1){	// Many sources
	
					if (targetCardinality > 1) {	// Many sources - Many targets
						
						// Creates one arc that connects each source to its corresponding target
						for (int i=0; i<sourceCardinality; i++){
							
							String sourceNodeId = cgim.getId()+sourceNode.getName().getText()+i;
							String targetNodeId = cgim.getId()+targetNode.getName().getText()+i;
							
							Node newSource = getConcreteNode(sourceNodeId, sourceNode);
							
							Node newTarget = getConcreteNode(targetNodeId, targetNode);
							
							Arc newArc = createArc(pnType, sourceNodeId+targetNodeId, newSource, newTarget, arc);
								
							newArcs.add(newArc);
							
						}
						
					} else if (targetCardinality <= 1){	// Many sources - One target
	
						// Creates one arc that connects each source to one target
						for (int i=0; i<sourceCardinality; i++){
							
							String sourceNodeId = cgim.getId()+sourceNode.getName().getText()+i;
							String targetNodeId = cgim.getId()+targetNode.getName().getText()+"0";
							
							Node newSource = getConcreteNode(sourceNodeId, sourceNode);
							
							Node newTarget = getConcreteNode(targetNodeId, targetNode);
							
							Arc newArc = createArc(pnType, sourceNodeId+targetNodeId, newSource, newTarget, arc);
								
							newArcs.add(newArc);
						}
	
					}
					
				} else if (sourceCardinality <= 1){	// One source
					
					if (targetCardinality > 1) {	// One source - Many targets
						
						// Creates one arc that connects each target to one source
						for (int i=0; i<targetCardinality; i++){
							
							String sourceNodeId = cgim.getId()+sourceNode.getName().getText()+"0";
							String targetNodeId = cgim.getId()+targetNode.getName().getText()+i;
							
							Node newSource = getConcreteNode(sourceNodeId, sourceNode);
							
							Node newTarget = getConcreteNode(targetNodeId, targetNode);
							
							Arc newArc = createArc(pnType, sourceNodeId+targetNodeId, newSource, newTarget, arc);
							
							newArcs.add(newArc);
							
						}
					} else if (targetCardinality <= 1){	// One source - One target
		
						// Creates one arc that connects one source and one target
	
						String sourceNodeId = cgim.getId()+sourceNode.getName().getText()+"0";
						String targetNodeId = cgim.getId()+targetNode.getName().getText()+"0";
						
						Node newSource = getConcreteNode(sourceNodeId, sourceNode);
						
						Node newTarget = getConcreteNode(targetNodeId, targetNode);
						
						Arc newArc = createArc(pnType, sourceNodeId+targetNodeId, newSource, newTarget, arc);
							
						newArcs.add(newArc);
	
					}
					
				}
				
			}

		}
		
		return newArcs;

	}
	
	private int getParallelCardinality(Node node){
		
		int cardinality = 1;
		
		// Get the cardinality of the arc's node
		if (node instanceof ar.edu.utn.frsf.cidisi.ginet.abstractginet.Place){
			
			// TODO: Check that the cardinality is not null, otherwise null pointer exception is raised.-
			cardinality = Integer.valueOf(((ar.edu.utn.frsf.cidisi.ginet.abstractginet.Place)node).getParallelCardinality()).intValue();
			
		} else if (node instanceof RefPlace){	// TODO: It is also necessary to include the RefTransition
			
			cardinality = Integer.valueOf(((RefPlace)node).getParallelCardinality()).intValue();
//			cardinality = 1;
			
		} else if (node instanceof ar.edu.utn.frsf.cidisi.ginet.Transition){
						
			cardinality = Integer.valueOf(((ar.edu.utn.frsf.cidisi.ginet.abstractginet.Transition)node).getParallelCardinality()).intValue();
			
		}
		
		return cardinality;
		
	}
	
	
	private int getSequentialCardinality(Node node){
		
		int cardinality = 1;
		
		// Get the cardinality of the arc's source
		if (node instanceof ar.edu.utn.frsf.cidisi.ginet.abstractginet.Transition){
			if (((ar.edu.utn.frsf.cidisi.ginet.abstractginet.Transition)node).getSequentialCardinality()!=null
					&& ((ar.edu.utn.frsf.cidisi.ginet.abstractginet.Transition)node).getSequentialCardinality()!=""){
			
				cardinality = Integer.valueOf(((ar.edu.utn.frsf.cidisi.ginet.abstractginet.Transition)node).getSequentialCardinality());
				
			}
				
		}
		
		return cardinality;
		
	}
	
	private Node getConcreteNode(String id, Node node){

		Node newNode = null;
		
		if (node instanceof ar.edu.utn.frsf.cidisi.ginet.abstractginet.Place){
			newNode = concretePlacesMap.get(id);
//			if (((ar.edu.utn.frsf.cidisi.ginet.abstractginet.Place)sourceNode).getRef()!=null)
//				newSource = concreteRefPlacesMap.get(idSource);
		}
		else if (node instanceof RefPlace)
			
			newNode = concreteRefPlacesMap.get(id);
		
		else if (node instanceof ar.edu.utn.frsf.cidisi.ginet.Transition)
			
			newNode = concreteTransitionsMap.get(id);
		
		return newNode;

	}


	/**
	 * Creates a set of concrete reference places according to the cardinality defined for each 
	 * reference place in the list abstract reference places.
	 * 
	 * @param abstractRefPlaces List of reference places of an abstract GI-Net
	 * @return List of concrete reference places 
	 *
	 * @author jmr
	 */
	private EList<org.pnml.tools.epnk.pnmlcoremodel.RefPlace> createRefPlaces(Page cgim, ar.edu.utn.frsf.cidisi.ginet.abstractginet.Page agim, int index, EList<RefPlace> abstractRefPlaces){
		EList<org.pnml.tools.epnk.pnmlcoremodel.RefPlace> nrp = new BasicEList<org.pnml.tools.epnk.pnmlcoremodel.RefPlace>();
		
		// For each place in the list of abstract places
		for (RefPlace abstractRefPlace : abstractRefPlaces){
			
			int card = 1;
			if (abstractRefPlace.getParallelCardinality() != null && !abstractRefPlace.getParallelCardinality().equals("")){
				card = Integer.valueOf(abstractRefPlace.getParallelCardinality()).intValue();
			}
			
			if (card > 1){  // Check the cardinality
				
				// Creates a number of places determined by the cardinality of the current abstract place
				for (int i=0; i<card; i++){
					
					Coordinate position = ((NodeGraphicsImpl)abstractRefPlace.getGraphics()).getPosition();
					
					String id = abstractRefPlace.getName().getText();
					
					float yPos = position.getY();
					
					id += i;		// Updates the id
					yPos += 90*i;	// Updates the y coordinate
					
					// Creates a new place PetriNetType pnType, String id, String name, int marking, float x, float y, Type placeType
					org.pnml.tools.epnk.pnmlcoremodel.RefPlace newRefPlace = createRefPlace(cgim.getId()+id, abstractRefPlace.getName().getText()+i, position.getX(), yPos);
					
					// Add the newly created place to a map in order to have direct access to such place
					concreteRefPlacesMap.put(newRefPlace.getId(), newRefPlace);
					
					nrp.add(newRefPlace);
					
				}
				
			} else { // Creates one place
				
				Coordinate position = ((NodeGraphicsImpl)abstractRefPlace.getGraphics()).getPosition();
				
				org.pnml.tools.epnk.pnmlcoremodel.RefPlace newRefPlace = createRefPlace(cgim.getId()+abstractRefPlace.getName().getText()+"0", abstractRefPlace.getName().getText()+"0", position.getX(), position.getY());

				concreteRefPlacesMap.put(newRefPlace.getId(), newRefPlace);

				nrp.add(newRefPlace);
				
			}
			
		}
	
		
		
		return nrp;
	}


	/**
	 * Creates a set of concrete ref places according to the cardinality defined for each 
	 * ref place in the list abstract ref places.
	 * 
	 * @param abstractRefPlaces List of ref places of an abstract GI-Net
	 * @return List of concrete ref places 
	 *
	 * @author jmr
	 */
/**	private EList<org.pnml.tools.epnk.pnmlcoremodel.RefPlace> createRefPlaces(Page cgim, ar.edu.utn.frsf.cidisi.ginet.abstractginet.Page agim, int index, EList<RefPlace> abstractRefPlaces){
		EList<org.pnml.tools.epnk.pnmlcoremodel.RefPlace> nrp = new BasicEList<org.pnml.tools.epnk.pnmlcoremodel.RefPlace>();
		
		// For each ref place in the list of abstract ref places
		for (ar.edu.utn.frsf.cidisi.ginet.abstractginet.RefPlace abstractRefPlace : abstractRefPlaces){
			
			if (abstractRefPlace.getRef() != null) { ACA YA EMEPZAMOS MAL PQ EN EL MODELO EL REFPLACE NO TIENE REFERENCIA A UN PLACE
				
//				ar.edu.utn.frsf.cidisi.ginet.Place abstractPlace = agim.getReferencedAbstractPlace(abstractRefPlace);
				ar.edu.utn.frsf.cidisi.ginet.abstractginet.Place abstractPlace = (ar.edu.utn.frsf.cidisi.ginet.abstractginet.Place)abstractRefPlace.getRef();
				ar.edu.utn.frsf.cidisi.ginet.abstractginet.Page subAgim = ((ar.edu.utn.frsf.cidisi.ginet.abstractginet.Page)abstractPlace.eContainer());
				ar.edu.utn.frsf.cidisi.ginet.abstractginet.Transition referringTransition = agim.getReferringTransition(subAgim, abstractRefPlace);
				
				int aTransitionParallelCard = 1;
				if (referringTransition.getParallelCardinality()!=null && referringTransition.getParallelCardinality()!="")
					aTransitionParallelCard = Integer.valueOf(referringTransition.getParallelCardinality());
				
//				String refPlaceId = page.getId()+abstractPlace.getName().getText()+index;
//				
//				return getPlace(page, refPlaceId.toLowerCase());
				
				int aPlaceParallelCard = 1;
				if (abstractPlace.getParallelCardinality() != null && !abstractPlace.getParallelCardinality().equals("")){
					
					// The cardinality of a RefPlace is the cardinality of its referenced Place.
					aPlaceParallelCard = Integer.valueOf(abstractPlace.getParallelCardinality()).intValue();
					
				}

				for (int transitionIdx = 0; transitionIdx<aTransitionParallelCard;transitionIdx++){
					Page referencedCgim = concreteSubPagesMap.get(cgim.getId()+subAgim.getId().toLowerCase()+transitionIdx);
//					Page referencedCgim = getConcretePage(cgim, agim, transitionIdx);
					
					if (aPlaceParallelCard > 1){  // Check the cardinality
						
						// Creates a number of ref places determined by the cardinality of the referenced place
						for (int placeIdx=0; placeIdx<aPlaceParallelCard; placeIdx++){
							
							Coordinate position = ((NodeGraphicsImpl)abstractRefPlace.getGraphics()).getPosition();
							
							String id = abstractRefPlace.getId();
							
							float yPos = position.getY();
							
							id += placeIdx;		// Updates the id
							yPos += 90*(placeIdx-1);	// Updates the y coordinate
		
							// Creates a new ref place
							org.pnml.tools.epnk.pnmlcoremodel.RefPlace newRefPlace = createRefPlace(cgim.getId()+id, abstractRefPlace.getName().getText()+"0", position.getX(), yPos);
		
							String refPlaceId = referencedCgim.getId()+abstractPlace.getName().getText()+placeIdx;
							Place place = concretePlacesMap.get(refPlaceId);
////							ACA HAY UN PROBLEMA YA QUE AL REFERENCED PLACE LO BUSCA EN LA PAGINA PADRE EN LUGAR DE BUSCARLO EN LA SUB PAGINA
//							Place place = getReferencedConcretePlace(referencedCgim, abstractPlace, placeIdx);
							newRefPlace.setRef(place);
							
							// Add the newly created ref place to a map in order to have direct access to such ref place
							concreteRefPlacesMap.put(newRefPlace.getId(), newRefPlace);
		
							nrp.add(newRefPlace);
							
						}
						
					} else {// Creates one ref place
						
						Coordinate position = ((NodeGraphicsImpl)abstractRefPlace.getGraphics()).getPosition();
						
						org.pnml.tools.epnk.pnmlcoremodel.RefPlace newRefPlace = createRefPlace(cgim.getId()+abstractRefPlace.getName().getText()+"0", abstractRefPlace.getName().getText()+"0", 
								position.getX(), position.getY());
		
						String refPlaceId = referencedCgim.getId()+abstractPlace.getName().getText()+index;
						Place concreteReferencedPlace = concretePlacesMap.get(refPlaceId);

						
//						Place concreteReferencedPlace = getReferencedConcretePlace(referencedCgim, abstractPlace, index);
						newRefPlace.setRef(concreteReferencedPlace);
		
						concreteRefPlacesMap.put(newRefPlace.getId(), newRefPlace);
		
						nrp.add(newRefPlace);
						
					}
					
				}
				
			}
			
		}
		
		return nrp;
	}

**/	
	
//	/**
//	 * Creates a set of concrete ref places according to the cardinality defined for each 
//	 * ref place in the list abstract ref places.
//	 * 
//	 * @param abstractRefPlaces List of ref places of an abstract GI-Net
//	 * @return List of concrete ref places 
//	 *
//	 * @author jmr
//	 */
//	private EList<RefPlace> createRefPlaces(Page cgim, ar.edu.utn.frsf.cidisi.ginet.Page agim, int index, EList<RefPlace> abstractRefPlaces){
//		EList<RefPlace> nrp = new BasicEList<RefPlace>();
//		
//		// For each ref place in the list of abstract ref places
//		for (RefPlace abstractRefPlace : abstractRefPlaces){
//			
//			if (abstractRefPlace.getRef() != null) {
//				
////				ar.edu.utn.frsf.cidisi.ginet.Place abstractPlace = ((ar.edu.utn.frsf.cidisi.ginet.Place)abstractRefPlace.getRef());
//				
//				ar.edu.utn.frsf.cidisi.ginet.Place abstractPlace = agim.getReferencedAbstractPlace(abstractRefPlace);
//				ar.edu.utn.frsf.cidisi.ginet.Page subAgim = ((ar.edu.utn.frsf.cidisi.ginet.Page)abstractPlace.eContainer());
//				ar.edu.utn.frsf.cidisi.ginet.Transition referringTransition = agim.getReferringTransition(subAgim, abstractRefPlace);
//				
//				int aTransitionParallelCard = 1;
//				if (referringTransition.getParallelCardinality()!=null && referringTransition.getParallelCardinality()!="")
//					aTransitionParallelCard = Integer.valueOf(referringTransition.getParallelCardinality());
//				
////				String refPlaceId = page.getId()+abstractPlace.getName().getText()+index;
////				
////				return getPlace(page, refPlaceId.toLowerCase());
//				
//				int aPlaceParallelCard = 1;
//				if (abstractPlace.getParallelCardinality() != null && !abstractPlace.getParallelCardinality().equals("")){
//					
//					// The cardinality of a RefPlace is the cardinality of its referenced Place.
//					aPlaceParallelCard = Integer.valueOf(abstractPlace.getParallelCardinality()).intValue();
//					
//				}
//
//				for (int transitionIdx = 0; transitionIdx<aTransitionParallelCard;transitionIdx++){
//					Page referencedCgim = concreteSubPagesMap.get(cgim.getId()+subAgim.getId().toLowerCase()+transitionIdx);
////					Page referencedCgim = getConcretePage(cgim, agim, transitionIdx);
//					
//					if (aPlaceParallelCard > 1){  // Check the cardinality
//						
//						// Creates a number of ref places determined by the cardinality of the referenced place
//						for (int placeIdx=0; placeIdx<aPlaceParallelCard; placeIdx++){
//							
//							Coordinate position = ((NodeGraphicsImpl)abstractRefPlace.getGraphics()).getPosition();
//							
//							String id = abstractRefPlace.getId();
//							
//							float yPos = position.getY();
//							
//							id += placeIdx;		// Updates the id
//							yPos += 90*(placeIdx-1);	// Updates the y coordinate
//		
//							// Creates a new ref place
//							RefPlace newRefPlace = createRefPlace(cgim.getId()+id, abstractRefPlace.getName().getText()+"0", position.getX(), yPos);
//		
//							String refPlaceId = referencedCgim.getId()+abstractPlace.getName().getText()+placeIdx;
//							Place place = concretePlacesMap.get(refPlaceId);
//////							ACA HAY UN PROBLEMA YA QUE AL REFERENCED PLACE LO BUSCA EN LA PAGINA PADRE EN LUGAR DE BUSCARLO EN LA SUB PAGINA
////							Place place = getReferencedConcretePlace(referencedCgim, abstractPlace, placeIdx);
//							newRefPlace.setRef(place);
//							
//							// Add the newly created ref place to a map in order to have direct access to such ref place
//							concreteRefPlacesMap.put(newRefPlace.getId(), newRefPlace);
//		
//							nrp.add(newRefPlace);
//							
//						}
//						
//					} else {// Creates one ref place
//						
//						Coordinate position = ((NodeGraphicsImpl)abstractRefPlace.getGraphics()).getPosition();
//						
//						RefPlace newRefPlace = createRefPlace(cgim.getId()+abstractRefPlace.getName().getText()+"0", abstractRefPlace.getName().getText()+"0", 
//								position.getX(), position.getY());
//		
//						String refPlaceId = referencedCgim.getId()+abstractPlace.getName().getText()+index;
//						Place concreteReferencedPlace = concretePlacesMap.get(refPlaceId);
//
//						
////						Place concreteReferencedPlace = getReferencedConcretePlace(referencedCgim, abstractPlace, index);
//						newRefPlace.setRef(concreteReferencedPlace);
//		
//						concreteRefPlacesMap.put(newRefPlace.getId(), newRefPlace);
//		
//						nrp.add(newRefPlace);
//						
//					}
//					
//				}
//				
//			}
//			
//		}
//		
//		return nrp;
//	}
	
	/**
	 * Returns a concrete page based on a referring transition and the referring transition's index
	 * 
	 * @param cgim
	 * @param referringTransition
	 * @param transitionIdx
	 * @return
	 *
	 * @author jmr
	 */
	private Page getConcretePage(Page cgim, Page agim, int transitionIdx){
		
		EList<org.pnml.tools.epnk.pnmlcoremodel.Object> elements = cgim.getObject();
		
		for (org.pnml.tools.epnk.pnmlcoremodel.Object element:elements){
			
			if (element instanceof Page){
				
				Page page = (Page)element;
				
				if (element.getId().equals(agim.getId()+transitionIdx))
					return page;
				
			}
			
		}
		
		return null;
	}
	
	/**
	 * 
	 * @param abstractRefPlace
	 * @return
	 *
	 * @author jmr
	 */
	private Place getReferencedConcretePlace(Page cgim, Place abstractPlace, int index){
		
		org.pnml.tools.epnk.pntypes.hlpng.pntd.hlpngdefinition.Page page = (org.pnml.tools.epnk.pntypes.hlpng.pntd.hlpngdefinition.Page)cgim.eContainer();
		
		if (page != null){

//			String refPlaceId = page.getName().getText()+refPlace.getId().substring((cgim.getName().getText()+"ref").length());
			String refPlaceId = page.getId()+abstractPlace.getName().getText()+index;
			
			return getConcretePlace(page, refPlaceId.toLowerCase());
			
		}
			
		return null;
	}
	
	
	/**
	 * 
	 * @param page
	 * @param id
	 * @return
	 *
	 * @author jmr
	 */
	private Place getConcretePlace(org.pnml.tools.epnk.pntypes.hlpng.pntd.hlpngdefinition.Page page, String id){
		
		for (Object element:page.getObject()){
			if (element instanceof Place){
				Place place = (Place)element;
				if (place.getId().equals(id)){
					return place;
				}
			}
		}
		
		return null;
	}

	
	/**
	 * Creates a set of concrete subpages according to the cardinality defined for each 
	 * subpage in the list abstract subpages.
	 * 
	 * @param agim An abstract GI-Net
	 * @param abstractSubPages List of subpages of the abstract GI-Net
	 * @return List of concrete subpages 
	 *
	 * @author jmr
	 */
	private EList<Page> createSubPages(Page parentCgim, EList<ar.edu.utn.frsf.cidisi.ginet.abstractginet.Page> abstractSubPages){
		EList<Page> np = new BasicEList<Page>();
		
		// For each place in the list of abstract subpages
		for (ar.edu.utn.frsf.cidisi.ginet.abstractginet.Page abstractSubPage : abstractSubPages){
			
//			int card = 1;
//			if (abstractSubPage.getCardinality() != null && !abstractSubPage.getCardinality().equals("")){
//				card = Integer.valueOf(abstractSubPage.getCardinality()).intValue();
//			}
//			
//			if (card > 1){	// Check the cardinality
//				
//				// Creates a number of subpages determined by the cardinality of the current abstract subpage
//				for (int i=1; i<=card; i++){
//					
//					Coordinate position = ((NodeGraphicsImpl)abstractSubPage.getGraphics()).getPosition();
//					
//					String id = abstractSubPage.getId();
//					
//					float yPos = position.getY();
//					
//					id += i;		// Updates the id
//					yPos += 90*(i-1);	// Updates the y coordinate
//
//					Page newCgim = createConcreteGIModule(i, parentCgim, abstractSubPage, position.getX(), yPos);
//					
////					// Adds the new concrete subpage to the parent page cgim
////					cgim.getObject().add(newCgim);
//
////					// Creates a new subpage
////					Page newSubPage = createPage(agim, id, id, position.getX(), yPos);
//					
//					// Add the newly created subpage to a map in order to have direct access to such subpage
//					concreteSubPagesMap.put(newCgim.getId(), newCgim);
//
//					np.add(newCgim);
//					
////					addConcreteGIModuleTo(subPage, cgim);
//					
//				}
//				
//			} else {	// Creates one subpage
				
				Coordinate position = ((NodeGraphicsImpl)abstractSubPage.getGraphics()).getPosition();
				
//				Page newSubPage = createPage(agim, subPage.getId(), subPage.getId(), 
//						position.getX(), position.getY());

//				addConcreteGIModuleTo(subPage, cgim);
				Page newCgim = createConcreteGIModule(0, parentCgim, abstractSubPage, position.getX(), position.getY());
				
//				// Adds the new concrete subpage to the parent page cgim
//				cgim.getObject().add(newCgim);
				

				concreteSubPagesMap.put(newCgim.getId(), newCgim);

				np.add(newCgim);
				
//			}
			
		}
		
		return np;
	}


	/**
	 * Creates a set of concrete subpages from referring transitions according to the cardinality defined for each 
	 * subpage in the list abstract subpages.
	 * 
	 * @param agim An abstract GI-Net
	 * @param referringTransitions List of subpages of the abstract GI-Net
	 * @return List of concrete subpages 
	 *
	 * @author jmr
	 */
	private EList<Page> createReferencedPages(Page parentCgim, Page agim, EList<ar.edu.utn.frsf.cidisi.ginet.abstractginet.Transition> referringTransitions){
		EList<Page> np = new BasicEList<Page>();
		
		// For each transition in the list of referring transitions
		for (ar.edu.utn.frsf.cidisi.ginet.abstractginet.Transition referringTransition : referringTransitions){
			
			int card = 1;
			if (referringTransition.getParallelCardinality() != null && !referringTransition.getParallelCardinality().equals("")){
				card = Integer.valueOf(referringTransition.getParallelCardinality()).intValue();
			}
			
//			int sequenceCardinality = 1;
//			if (referringTransition.getRefPage().getSequenceCardinality()!=null 
//					&& referringTransition.getRefPage().getSequenceCardinality()!="")
//				sequenceCardinality = Integer.valueOf(referringTransition.getRefPage().getSequenceCardinality()).intValue();
//			
//			if (sequenceCardinality!=1){
//
//				Page newCgim = createSequencedConcreteGIModule(parentCgim, referringTransition, card);
//				
//				// Add the newly created subpage to a map in order to have direct access to such subpage
//				concreteSubPagesMap.put(newCgim.getId(), newCgim);
//
//				np.add(newCgim);
//
//				
//			} else 
			if (card > 1){	// Check the cardinality
				
				// Creates a number of subpages determined by the cardinality of the current referring transition
				for (int i=0; i<card; i++){
					
					Coordinate position = ((NodeGraphicsImpl)referringTransition.getGraphics()).getPosition();
					
					String id = referringTransition.getId();
					
					float yPos = position.getY();
					
					id += i;		// Updates the id
					yPos += 90*(i-1);	// Updates the y coordinate

					
					int sequenceCardinality = 1;
					if (referringTransition.getSequentialCardinality()!=null 
							&& referringTransition.getSequentialCardinality()!="")
						sequenceCardinality = Integer.valueOf(referringTransition.getSequentialCardinality()).intValue();
					
					if (sequenceCardinality!=1){
		
						Page newCgim = createSequencialConcreteGIModule(i, parentCgim, (ar.edu.utn.frsf.cidisi.ginet.abstractginet.Page)referringTransition.getRefPage(), position.getX(), yPos, agim, referringTransition, card);
						
						// Add the newly created subpage to a map in order to have direct access to such subpage
						concreteSubPagesMap.put(newCgim.getId(), newCgim);
		
						np.add(newCgim);
		
						
					} else 	{			
						Page newCgim = createConcreteGIModule(i, parentCgim, (ar.edu.utn.frsf.cidisi.ginet.abstractginet.Page)referringTransition.getRefPage(), position.getX(), yPos);
					
//						// Adds the new concrete subpage to the parent page cgim
//						cgim.getObject().add(newCgim);

//						// Creates a new subpage
//						Page newSubPage = createPage(agim, id, id, position.getX(), yPos);
					
						// Add the newly created subpage to a map in order to have direct access to such subpage
						concreteSubPagesMap.put(newCgim.getId(), newCgim);
						
						np.add(newCgim);
						
//						addConcreteGIModuleTo(subPage, cgim);
					}
					
				}
				
			} else {	// Creates one subpage
				
				int sequenceCardinality = 1;
				if (referringTransition.getSequentialCardinality()!=null 
						&& referringTransition.getSequentialCardinality()!="")
					sequenceCardinality = Integer.valueOf(referringTransition.getSequentialCardinality()).intValue();
				
				if (sequenceCardinality!=1){

					Coordinate position = ((NodeGraphicsImpl)referringTransition.getGraphics()).getPosition();

					Page newCgim = createSequencialConcreteGIModule(0, parentCgim, (ar.edu.utn.frsf.cidisi.ginet.abstractginet.Page)referringTransition.getRefPage(), position.getX(), position.getY(), agim, referringTransition, card);
					
					// Add the newly created subpage to a map in order to have direct access to such subpage
					concreteSubPagesMap.put(newCgim.getId(), newCgim);
	
					np.add(newCgim);
	
					
				} else 	{			
					Coordinate position = ((NodeGraphicsImpl)referringTransition.getGraphics()).getPosition();
					
//					Page newSubPage = createPage(agim, subPage.getId(), subPage.getId(), 
//							position.getX(), position.getY());

//					addConcreteGIModuleTo(subPage, cgim);
					Page newCgim = createConcreteGIModule(0, parentCgim, (ar.edu.utn.frsf.cidisi.ginet.abstractginet.Page)referringTransition.getRefPage(), position.getX(), position.getY());
				
//					// Adds the new concrete subpage to the parent page cgim
//					cgim.getObject().add(newCgim);
					
	
					concreteSubPagesMap.put(newCgim.getId(), newCgim);
	
					np.add(newCgim);
				}
				
			}
			
		}
		
		return np;
	}
	
	
	/**
	 * Creates a new place.
	 * 
	 * @param pnType
	 * @param id
	 * @param name
	 * @param marking
	 * @param x
	 * @param y
	 * @param placeType
	 * @return
	 *
	 * @author jmr
	 */
	private Place createPlace(PetriNetType pnType, String id, String name, int marking, float x, float y, Type placeType) {
		
		Place newPlace = (Place) pnType.createPlace();
		
		newPlace.setId(id);	// Set the id
		
		NodeGraphics nodeGraphics = PnmlcoremodelFactory.eINSTANCE.createNodeGraphics();
		
		// Creates a coordinate position for the place
		Coordinate position = PnmlcoremodelFactory.eINSTANCE.createCoordinate();
		position.setX(x);
		position.setY(y);
		nodeGraphics.setPosition(position);
		
		// Size of the place
		Coordinate size = PnmlcoremodelFactory.eINSTANCE.createCoordinate();
		size.setX(40);
		size.setY(40);
		nodeGraphics.setDimension(size);
		
		newPlace.setGraphics(nodeGraphics);
		
		// Creates a label for the place
		Name nameLabel = PnmlcoremodelFactory.eINSTANCE.createName();
		nameLabel.setText(name);
		AnnotationGraphics labelPos = PnmlcoremodelFactory.eINSTANCE.createAnnotationGraphics();
		Coordinate offset = PnmlcoremodelFactory.eINSTANCE.createCoordinate();
		offset.setX(-20);
		offset.setY(-40);
		labelPos.setOffset(offset);
		nameLabel.setGraphics(labelPos);
		
		newPlace.setName(nameLabel);
		
		// Creates the type of the place
		Type newPlaceType = HlpngdefinitionFactory.eINSTANCE.createType();
		
		if (placeType != null && placeType.getStructure() instanceof UserSort){
			
			UserSort userSort = (UserSort)placeType.getStructure();
			
			UserSort newUserSort = TermsFactory.eINSTANCE.createUserSort();
			
			if (userSort.getDeclaration() instanceof NamedSort){
				
				NamedSort innerDecl = (NamedSort)userSort.getDeclaration();
				
				NamedSort newDecl = TermsFactory.eINSTANCE.createNamedSort();
				
				if (innerDecl.getDef() instanceof org.pnml.tools.epnk.pntypes.hlpngs.datatypes.integers.Integer){
					
					org.pnml.tools.epnk.pntypes.hlpngs.datatypes.integers.Integer integer = IntegersFactory.eINSTANCE.createInteger();
					
					newDecl.setDef(integer);
					
				} 
				newDecl.setId(innerDecl.getId());
				
				newDecl.setName(innerDecl.getName());
				
				newUserSort.setDeclaration(newDecl);
				
			} 

			newPlaceType.setStructure(newUserSort);
			newPlaceType.setText(placeType.getText());
			
			// Creates a coordinate position for the place type
			AnnotationGraphics newPlaceTypePos = PnmlcoremodelFactory.eINSTANCE.createAnnotationGraphics();
			Coordinate newPlaceTypeOffset = PnmlcoremodelFactory.eINSTANCE.createCoordinate();
			newPlaceTypeOffset.setX(0);
			newPlaceTypeOffset.setY(30);
			newPlaceTypePos.setOffset(newPlaceTypeOffset);
			newPlaceType.setGraphics(newPlaceTypePos);
			
			newPlace.setType(newPlaceType);
		}
		
		
//			IntegersFactory.eINSTANCE.createInteger();
//			
//			Declaration decl = HlpngdefinitionFactory.eINSTANCE.createDeclaration();
//			decl.g
			
			
//		if (marking > 0) {
//			PTMarking markingLabel = PtnetFactory.eINSTANCE.createPTMarking();
//			NonNegativeInteger value = new NonNegativeInteger(""+marking);
//			markingLabel.setText(value);
//			labelPos = PnmlcoremodelFactory.eINSTANCE.createAnnotationGraphics();
//			offset = PnmlcoremodelFactory.eINSTANCE.createCoordinate();
//			offset.setX(0);
//			offset.setY(-40);
//			labelPos.setOffset(offset);
//			markingLabel.setGraphics(labelPos);
//			place.setInitialMarking(markingLabel);
//		}
		
		return newPlace;
	}
	
	/**
	 * Creates a new transition
	 * 
	 * @param type
	 * @param id
	 * @param name
	 * @param x
	 * @param y
	 * @return
	 *
	 * @author jmr
	 */
	private Transition createTransition(PetriNetType type, String id, String name, float x, float y) {
		
		Transition transition = (Transition)type.createTransition();
		transition.setId(id);	// Set the transition's id
		
		NodeGraphics nodeGraphics = PnmlcoremodelFactory.eINSTANCE.createNodeGraphics();

		// Creates a coordinate position for the transition
		Coordinate position = PnmlcoremodelFactory.eINSTANCE.createCoordinate();
		position.setX(x);
		position.setY(y);
		nodeGraphics.setPosition(position);
		
		// Set the size of the transition
		Coordinate size = PnmlcoremodelFactory.eINSTANCE.createCoordinate();
		size.setX(40);
		size.setY(40);
		nodeGraphics.setDimension(size);
		
		transition.setGraphics(nodeGraphics);
		
		// Creates a label for the transition
		Name nameLabel = PnmlcoremodelFactory.eINSTANCE.createName();
		nameLabel.setText(name);
		AnnotationGraphics labelPos = PnmlcoremodelFactory.eINSTANCE.createAnnotationGraphics();
		Coordinate offset = PnmlcoremodelFactory.eINSTANCE.createCoordinate();
		offset.setX(0);
		offset.setY(30);
		labelPos.setOffset(offset);
		nameLabel.setGraphics(labelPos);
		transition.setName(nameLabel);
		
		return transition;
	}
	
	/**
	 * Creates a new arc.
	 * 
	 * @param type
	 * @param id
	 * @param source
	 * @param target
	 * @param arc
	 * @return
	 *
	 * @author jmr
	 */
	private Arc createArc(PetriNetType type, String id, Node source, Node target, Arc arc) {
		
		Arc newArc = (Arc)type.createArc();
		
		newArc.setId(id);
		
		newArc.setSource(source);
		
		newArc.setTarget(target);
		
		HLAnnotation annotation = HlpngdefinitionFactory.eINSTANCE.createHLAnnotation();
		AnnotationGraphics labelPos = PnmlcoremodelFactory.eINSTANCE.createAnnotationGraphics();
		Coordinate offset = PnmlcoremodelFactory.eINSTANCE.createCoordinate();
		offset.setX(0);
		offset.setY(30);
		labelPos.setOffset(offset);
		annotation.setGraphics(labelPos);
		
		HLAnnotation hlAnnotation = arc.getHlinscription();
		if (hlAnnotation != null){
			Variable newVar = TermsFactory.eINSTANCE.createVariable();
			
			
			if (hlAnnotation.getStructure() instanceof Variable){
				Variable var = (Variable)hlAnnotation.getStructure();
			
				VariableDecl varDecl = TermsFactory.eINSTANCE.createVariableDecl();
				varDecl.setId(var.getRefvariable().getId());
				varDecl.setName(var.getRefvariable().getName());
				varDecl.setSort(var.getSort().clone());
			
				newVar.setRefvariable(varDecl);
				newVar.setName(var.getName());

			}
			annotation.setStructure(newVar);
			annotation.setText(hlAnnotation.getText());
		}
		else{
			annotation.setText("");
		}
		
		newArc.setHlinscription(annotation);
		
//		Name nameLabel = PnmlcoremodelFactory.eINSTANCE.createName();
//		nameLabel.setText("");
//		AnnotationGraphics labelPos = PnmlcoremodelFactory.eINSTANCE.createAnnotationGraphics();
//		Coordinate offset = PnmlcoremodelFactory.eINSTANCE.createCoordinate();
//		offset.setX(0);
//		offset.setY(30);
//		labelPos.setOffset(offset);
//		nameLabel.setGraphics(labelPos);
//		arc.setName(nameLabel);
		
//		Coordinate coordinate = PnmlcoremodelFactory.eINSTANCE.createCoordinate();
//		coordinate.setX(300);
//		coordinate.setY(60);
//		ArcGraphics arcGraphics = PnmlcoremodelFactory.eINSTANCE.createArcGraphics();
//		arcGraphics.getPosition().add(coordinate);
//		arc.setGraphics(arcGraphics);
		
		return newArc;	
	}
	
	
	/**
	 * 
	 * @param id
	 * @param name
	 * @param x
	 * @param y
	 * @return
	 *
	 * @author jmr
	 */
	private org.pnml.tools.epnk.pnmlcoremodel.RefPlace createRefPlace(String id, String name, float x, float y) {
		org.pnml.tools.epnk.pnmlcoremodel.RefPlace refPlace = PnmlcoremodelFactory.eINSTANCE.createRefPlace();
		refPlace.setId(id);	// Set the ref places's id
		
		NodeGraphics nodeGraphics = PnmlcoremodelFactory.eINSTANCE.createNodeGraphics();

		// Creates a coordinate position for the ref place
		Coordinate position = PnmlcoremodelFactory.eINSTANCE.createCoordinate();
		position.setX(x);
		position.setY(y);
		nodeGraphics.setPosition(position);
		
		// Set the size of the ref place
		Coordinate size = PnmlcoremodelFactory.eINSTANCE.createCoordinate();
		size.setX(40);
		size.setY(40);
		nodeGraphics.setDimension(size);
		
		refPlace.setGraphics(nodeGraphics);
		
		// Creates a label for the ref place
		Name nameLabel = PnmlcoremodelFactory.eINSTANCE.createName();
		nameLabel.setText(name);
		AnnotationGraphics labelPos = PnmlcoremodelFactory.eINSTANCE.createAnnotationGraphics();
		Coordinate offset = PnmlcoremodelFactory.eINSTANCE.createCoordinate();
		offset.setX(0);
		offset.setY(30);
		labelPos.setOffset(offset);
		nameLabel.setGraphics(labelPos);
		refPlace.setName(nameLabel);
		
		return refPlace;
	}

	
	/**
	 * 
	 * @param agim
	 * @param id
	 * @param name
	 * @param x
	 * @param y
	 * @return
	 *
	 * @author jmr
	 */
	private Page createPage(Page agim, String id, String name, float x, float y) {
		
		Page page = (Page)pnType.createPage();
		page.setId(id);	// Set the page's id
		
		NodeGraphics nodeGraphics = PnmlcoremodelFactory.eINSTANCE.createNodeGraphics();

		// Creates a coordinate position for the page
		Coordinate position = PnmlcoremodelFactory.eINSTANCE.createCoordinate();
		position.setX(x);
		position.setY(y);
		nodeGraphics.setPosition(position);
		
		// Set the size of the page
		Coordinate size = PnmlcoremodelFactory.eINSTANCE.createCoordinate();
		size.setX(40);
		size.setY(40);
		nodeGraphics.setDimension(size);
		
		page.setGraphics(nodeGraphics);
		
		// Creates a label for the page
		Name nameLabel = PnmlcoremodelFactory.eINSTANCE.createName();
		nameLabel.setText(name);
		AnnotationGraphics labelPos = PnmlcoremodelFactory.eINSTANCE.createAnnotationGraphics();
		Coordinate offset = PnmlcoremodelFactory.eINSTANCE.createCoordinate();
		offset.setX(0);
		offset.setY(30);
		labelPos.setOffset(offset);
		nameLabel.setGraphics(labelPos);
		page.setName(nameLabel);
		
		return page;
	}
	
	
	public PetriNetDoc getPetriNetDoc() {
		return petriNetDoc;
	}

	public GINet getConcreteGINet() {
		return concreteGINet;
	}


}
